package cn.pku.edu.cs.ldn.thread;

import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import cn.pku.edu.cs.ldn.R;
import cn.pku.edu.cs.ldn.util.BitmapCacheUtil;
import cn.pku.edu.cs.ldn.util.ImageUtil;

import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.ImageView;

public class BitmapLoader {
	private static final String TAG = "BitmapLoader";
	private static final boolean DEBUG = true;

	private Map<String, List<WeakReference<ImageView>>> loadingMap = null;

	private static BitmapLoader instance = null;

	public static BitmapLoader getInstance() {
		synchronized (BitmapLoader.class) {
			if (instance == null) {
				instance = new BitmapLoader();
			}
			return instance;
		}
	}

	public void requestLoadBitmap(ImageView imageView, String localPath,
			int reqWidth, int reqHeight) {
		if (DEBUG)
			Log.v(TAG, "requestLoadBitmap: " + localPath + " " + reqWidth + " "
					+ reqHeight);
		if (imageView == null || localPath == null) {
			return;
		}
		String bitmapKey = BitmapCacheUtil.getBitmapKey(localPath, reqWidth,
				reqHeight);
		imageView.setTag(R.string.key_imageview, bitmapKey);
		Bitmap bitmap = BitmapCacheUtil.getBitmapFromMemoryCache(bitmapKey);
		if (bitmap != null) {
			if (DEBUG)
				Log.v(TAG, "requestLoadBitmap: cache hit");
			imageView.setImageBitmap(bitmap);
			return;
		}

		// no cache found, so check if this bitmap is already in loading
		List<WeakReference<ImageView>> list = null;
		if (loadingMap != null) {
			list = loadingMap.get(bitmapKey);
			if (list != null && !list.isEmpty()) {
				// this bitmap is already in loading
				list.add(new WeakReference<ImageView>(imageView));
				loadingMap.put(bitmapKey, list);
				return;
			}
		} else {
			loadingMap = new HashMap<String, List<WeakReference<ImageView>>>();
		}

		// this bitmap is NOT in loading, so load it async
		list = new LinkedList<WeakReference<ImageView>>();
		list.add(new WeakReference<ImageView>(imageView));
		loadingMap.put(bitmapKey, list);
		BitmapAsyncLoader bitmapAsyncLoader = new BitmapAsyncLoader(localPath,
				reqWidth, reqHeight);
		bitmapAsyncLoader.execute();
	}

	class BitmapAsyncLoader extends AsyncTask<Void, Void, Void> {

		private String localPath = null;
		private int reqWidth;
		private int reqHeight;
		private Bitmap bitmap = null;

		public BitmapAsyncLoader(String localPath, int reqWidth, int reqHeight) {
			this.localPath = localPath;
			this.reqWidth = reqWidth;
			this.reqHeight = reqHeight;
		}

		@Override
		protected Void doInBackground(Void... params) {
			if (localPath == null) {
				return null;
			}
			bitmap = ImageUtil.loadBitmapNoBiggerThanRequired(localPath,
					reqWidth, reqHeight);
			return null;
		}

		@Override
		protected void onPostExecute(Void result) {
			if (bitmap == null || loadingMap == null) {
				if (DEBUG)
					Log.e(TAG, "onPostExecute: error");
				return;
			}
			String bitmapKey = BitmapCacheUtil.getBitmapKey(localPath,
					reqWidth, reqHeight);
			BitmapCacheUtil.addBitmapToMemoryCache(bitmapKey, bitmap);

			// set the bitmap to the ImageViews that have requested it
			List<WeakReference<ImageView>> list = loadingMap.get(bitmapKey);
			if (list == null || list.isEmpty())
				return;
			for (WeakReference<ImageView> w : list) {
				ImageView imageView = w.get();
				if (imageView != null) {
					String tag = (String) imageView
							.getTag(R.string.key_imageview);
					if (tag != null && tag.equals(bitmapKey)) {
						imageView.setImageBitmap(bitmap);
					} else {
						if (DEBUG)
							Log.i(TAG,
									"onPostExecute: the imageView no longer want this bitmap");
					}
				} else {
					if (DEBUG)
						Log.i(TAG,
								"onPostExecute: the imageView has been recycled");
				}
			}
			loadingMap.remove(bitmapKey);
		}
	}
}
